﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

namespace MessengerBot
{
    /// <summary>
    /// A single log entry to be logged to a <see cref="ILogger"/> destination. This
    /// struct is used by the <see cref="Predicate{T}"/> passed to a <see cref="ILogger"/>
    /// implementation, to filter log messages.
    /// </summary>
    public struct LogEntry : IEquatable<LogEntry>
    {
        /// <summary>
        /// This is the backing field for the <see cref="Level"/> property.
        /// </summary>
        private readonly LogLevel _Level;

        /// <summary>
        /// This is the backing field for the <see cref="Message"/> property.
        /// </summary>
        private readonly string _Message;

        /// <summary>
        /// Initializes a new instance of the <see cref="LogEntry"/> struct.
        /// </summary>
        /// <param name="level">
        /// The level of the message, used for filtering.
        /// </param>
        /// <param name="message">
        /// The message to log.
        /// </param>
        public LogEntry(LogLevel level, string message)
        {
            _Level = level;
            _Message = message;
        }

        /// <summary>
        /// Gets the message to log.
        /// </summary>
        public string Message
        {
            get
            {
                return _Message;
            }
        }

        /// <summary>
        /// Gets the level of the message.
        /// </summary>
        public LogLevel Level
        {
            get
            {
                return _Level;
            }
        }

        #region IEquatable<LogEntry> Members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(LogEntry other)
        {
            return Equals(other._Level, _Level) && Equals(other._Message, _Message);
        }

        #endregion

        /// <summary>
        /// Indicates whether this instance and a specified object are equal.
        /// </summary>
        /// <returns>
        /// <c>true</c> if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
        /// </returns>
        /// <param name="obj">
        /// Another object to compare to.
        /// </param>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (obj.GetType() != typeof(LogEntry)) return false;
            return Equals((LogEntry)obj);
        }

        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that is the hash code for this instance.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                return (_Level.GetHashCode() * 397) ^ (_Message != null ? _Message.GetHashCode() : 0);
            }
        }

        /// <summary>
        /// Returns the fully qualified type name of this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="String"/> containing a fully qualified type name.
        /// </returns>
        public override string ToString()
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", _Level, _Message);
        }
    }
}